#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <cliijpege.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define DEFALUT_OUTFILE_NAME		"out.jpege"

static int select_input(char *input_filename, cli_opt_t *opt)
{
	char *ext = get_filename_extension(input_filename);
	if (strcmp(ext, "yuv") == 0) {
		opt->i_csp = C_CSP_I420;
	} else if (strcmp(ext, "nv21") == 0) {
		opt->i_csp = C_CSP_NV21;
	} else if (strcmp(ext, "nv12") == 0) {
		opt->i_csp = C_CSP_NV12;
	} else if (strcmp(ext, "t420") == 0) {
		opt->i_csp = C_CSP_T420;
	} else {
		fprintf(stderr, "can't supported input file color space\n");
		goto err_unsupport_csp;
	}
	if (opt->i_csp <= C_CSP_T420) {
		opt->cli_input = ((cli_input_t *)&cli_input_nv12);
	} else {
		fprintf(stderr, "unsupport source colorspace\n");
		goto err_unsupport_source_csp;
	}

	return 0;

err_unsupport_source_csp:
err_unsupport_csp:
	return -1;
}

static int parse(int argc, char *argv[], ijpege_param_t *param, cli_opt_t *opt)
{
	ijpege_param_default(param);
    param->soc = atoi(argv[1]);
    if ((param->soc>=C_SOC_MAX)||(param->soc<0))
    {
		fprintf(stderr, "soc failed\n");
        goto err_select_ensoc;
    }
    param->i_width = opt->image_width = atoi(argv[3]);
	param->i_height = opt->image_height = atoi(argv[4]);
	opt->b_use_phy = (param->b_use_phy = true);
	if (param->b_use_phy) {
		opt->ckmalloc = param->ckmalloc = c_kalloc;
		opt->ckfree = param->ckfree = c_kfree;
		opt->ckvirt_to_phys = param->ckvirt_to_phys = c_kvirt_to_kphys;
		opt->ckphys_to_virt = param->ckphys_to_virt = c_kphys_to_kvirt;
	} else {
		opt->ckmalloc = param->ckmalloc = c_malloc;
		opt->ckfree = param->ckfree = c_free;
		opt->ckvirt_to_phys = param->ckvirt_to_phys = c_virt_to_phys;
		opt->ckphys_to_virt = param->ckphys_to_virt = c_phys_to_virt;
	}
	opt->start_frame = atoi(argv[5]);
	opt->sum_frames = atoi(argv[6]);

	if (select_input(argv[2], opt) < 0) {
		fprintf(stderr, "select_input failed\n");
		goto err_select_input;
	}
	if (opt->cli_input->open_file(argv[2], opt) < 0) {
		fprintf(stderr, "cli_input open failed failed\n");
		goto err_cli_open_inputfile;
	}
    if (argc >= 8) {
		int size = strlen(argv[7]) + 20;
		opt->output_file_name = malloc(size);
		strcpy(opt->output_file_name, argv[7]);
	} else {
		int size = strlen(DEFALUT_OUTFILE_NAME) + 20;
		opt->output_file_name = malloc(size);
		strcpy(opt->output_file_name, DEFALUT_OUTFILE_NAME);
	}

    if (argc >= 9) {
        opt->i_ql = atoi(argv[8]);
    } else {
        opt->i_ql = -1;
    }

	return 0;

	opt->cli_input->close_file(opt);
err_select_ensoc:
err_cli_open_inputfile:
err_select_input:
	return -1;
}

int init(int argc, char **argv, ijpege_param_t *param, cli_opt_t *opt)
{
	if (argc < 7) {
		fprintf(stderr, "Usage : %s soc inputfile width height startframe sumframes [outputfile] [ql]\n", argv[0]);
		fprintf(stderr, "        input number,4 :C_T21 3 :C_T30 2 :C_T20 1 :C_T10 0 :C_M200 \n");
		fprintf(stderr, "        startframe means frame location in the input file\n");
		fprintf(stderr, "        sumframes means frame num started from startframe to be encode\n");
		goto err_invalidate_cmdline;
	}

	if (parse(argc, argv, param, opt) < 0) {
		fprintf(stderr, "parse param failed\n");
		goto err_parse_param;
	}
	return 0;

err_parse_param:
err_invalidate_cmdline:
	return -1;
}

void deinit(ijpege_param_t *param, cli_opt_t *opt)
{
	opt->cli_input->close_file((cli_opt_t *)opt);
	free(opt->output_file_name);
	opt->output_file_name = NULL;
}

static const int jpeg_chroma_quantizer[64] = {
	17, 18, 24, 47, 99, 99, 99, 99,
	18, 21, 26, 66, 99, 99, 99, 99,
	24, 26, 56, 99, 99, 99, 99, 99,
	47, 66, 99, 99, 99, 99, 99, 99,
	99, 99, 99, 99, 99, 99, 99, 99,
	99, 99, 99, 99, 99, 99, 99, 99,
	99, 99, 99, 99, 99, 99, 99, 99,
	99, 99, 99, 99, 99, 99, 99, 99
};

static const int jpeg_luma_quantizer[64] = {
	16, 11, 10, 16, 24, 40, 51, 61,
	12, 12, 14, 19, 26, 58, 60, 55,
	14, 13, 16, 24, 40, 57, 69, 56,
	14, 17, 22, 29, 51, 87, 80, 62,
	18, 22, 37, 56, 68, 109, 103, 77,
	24, 35, 55, 64, 81, 104, 113, 92,
	49, 64, 78, 87, 103, 121, 120, 101,
	72, 92, 95, 98, 112, 100, 103, 99
};

void MakeTables(int q, uint8_t *lqt, uint8_t *cqt)
{
	int i;
	int factor = q;
	if (q < 1) factor = 1;
	if (q > 99) factor = 99;
	if (q < 50)
		q = 5000 / factor;
	else
		q = 200 - factor*2;
	for (i=0; i < 64; i++) {
		int lq = (jpeg_luma_quantizer[i] * q + 50) / 100;
		int cq = (jpeg_chroma_quantizer[i] * q + 50) / 100;
		/* Limit the quantizers to 1 <= q <= 255 */
		if (lq < 1) lq = 1;
		else if (lq > 255) lq = 255;
		lqt[i] = lq;
		if (cq < 1) cq = 1;
		else if (cq > 255) cq = 255;
		cqt[i] = cq;
	}
}

int encode(ijpege_param_t *param, cli_opt_t *opt, volatile const int *ctl, ijpege_ql_param_t *ql_param)
{
	ijpege_t *h = NULL;
	ijpege_pic_t pic_in, pic_out;
	ijpege_nal_t *p_nal = NULL;
	int i_nal = 0;
	int i = 0, j = 0;
	int file_name_len = 0;
	if (opt->cli_input->picture_alloc(&pic_in, opt) < 0) {
		fprintf(stderr, "picture_alloc failed\n");
		goto err_picture_alloc;
	}
	if ((h = ijpege_init(param)) == NULL) {
		fprintf(stderr, "ijpege_init failed\n");
		goto err_ijpege_init;
	}

    if (ql_param && ijpege_set_param(h, IJPEGE_RCFG_QL_ID, ql_param) < 0) {
		fprintf(stderr, "ijpege_set_param ql failed\n");
		goto err_ijpege_set_param_ql;
    }

	file_name_len = strlen(opt->output_file_name);
	for (i = 0; !(*ctl) && (i < opt->sum_frames); i++) {
		char name_suffix[20];

		if (opt->cli_input->read_frame(&pic_in, opt) < 0) {
			fprintf(stderr, "read_frame failed\n");
			goto err_read_frame;
		}


		if (ijpege_encode(h, &p_nal, &i_nal, &pic_in, &pic_out) < 0) {
			fprintf(stderr, "ijpege_encode failed\n");
			goto err_ijpege_encode;
		}

		opt->output_file_name[file_name_len] = 0;
		sprintf(name_suffix, "_%d.jpg", i+1);
		strcat(opt->output_file_name, name_suffix);
		if ((opt->output_file  = fopen(opt->output_file_name, "w+")) == NULL) {
			fprintf(stderr, "fopen %s failed:%s\n", opt->output_file_name, strerror(errno));
			goto err_open_outputfile;
		}
		for (j = 0; j < i_nal; j++) {
		/* write encoder data */
			if (fwrite(p_nal[j].p_payload, 1, p_nal[j].i_payload, opt->output_file)
					!= p_nal[j].i_payload) {
				fprintf(stderr, "fwrite p_nal->p_payload:%p bytes:%d failed:%s\n",
						p_nal[j].p_payload, p_nal[j].i_payload, strerror(errno));
				goto err_fwrite_nal;
			}
		}
		fclose(opt->output_file);
	}

	if (opt->cli_input->release_frame(&pic_in, opt) < 0) {
		goto err_release_frame;
	}
	ijpege_deinit(h);
	opt->cli_input->picture_clean(&pic_in, opt);

	return 0;

err_release_frame:
err_fwrite_nal:
err_open_outputfile:
err_ijpege_encode:
	opt->cli_input->release_frame(&pic_in, opt);
err_read_frame:
err_ijpege_set_param_ql:
	ijpege_deinit(h);
err_ijpege_init:
	opt->cli_input->picture_clean(&pic_in, opt);
err_picture_alloc:
	return -1;
}
